//
//  XMQHttpTransfer.m
//  XiaoMiQuan
//
//  Created by liaowei on 14-4-18.
//
//

#import "XMQHttpTransfer.h"

#define XMQ_HTTP_USER_AGENT  @"XKJH/iOS"


@interface XMQHttpTransfer ()
{
    NSURLConnection *_conn;
    NSMutableURLRequest *_req;
    NSNumber *_requestId;    // 本次请求的 id ，由用户定义
    id _userObj;   // 用户自定义的对象
    NSInputStream *_bodyStream;
    NSOutputStream  *_responseStream;
    NSError *_error;
}

@property (strong, nonatomic) id<XMQHttpFileTransferDelegate> delegate;

@end


@implementation XMQHttpTransfer

- (id)init
{
    self = [super init];
    
    return self;
}

- (id)initWithRequest:(NSNumber *)requestId
              userObj:(id)userObj
               method:(NSString *)method
                  url:(NSString *)url
             withBody:(NSData *)body
{
    self = [self init];
    if (self != nil) {
        _requestId = requestId;
        _userObj = userObj;
        
        _req = [NSMutableURLRequest requestWithURL:[[NSURL alloc] initWithString:[url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
        [_req setHTTPMethod:method];
        [_req setHTTPBody:body];
        [_req setValue:XMQ_HTTP_USER_AGENT forHTTPHeaderField:@"User-Agent"];
        [_req setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
    }
    
    return self;
}

- (id)initWithRequest:(NSNumber *)requestId
              userObj:(id)userObj
               method:(NSString *)method
                  url:(NSString *)url
             withBodyFile:(NSString *)bodyFile
{
    self = [self init];
    if (self != nil) {
        _requestId = requestId;
        _userObj = userObj;
        _bodyStream = [[NSInputStream alloc] initWithFileAtPath:bodyFile];

        NSNumber *contentLength = (NSNumber *) [[[NSFileManager defaultManager] attributesOfItemAtPath:bodyFile error:NULL] objectForKey:NSFileSize];
        
        _req = [NSMutableURLRequest requestWithURL:[[NSURL alloc] initWithString:[url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
        [_req setHTTPMethod:method];
        [_req setHTTPBodyStream:_bodyStream];
        [_req setValue:[contentLength description] forHTTPHeaderField:@"Content-Length"];
        [_req setValue:@"application/octet-stream" forHTTPHeaderField:@"Content-Type"];
        [_req setValue:XMQ_HTTP_USER_AGENT forHTTPHeaderField:@"User-Agent"];
        [_req setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
    }
    
    return self;
}

#pragma mark -- NSOperation

- (BOOL)isConcurrent
{
    return YES;
}

- (BOOL)isExecuting
{
    return _conn != nil;
}

- (BOOL)isFinished
{
    return _conn == nil;
}

- (void)cancel
{
    if (_conn != nil) {
        [_conn cancel];
        _conn = nil;
    }
}

- (void)start
{
    if ([self isCancelled]) {
        return;
    }
    
    _conn = [[NSURLConnection alloc] initWithRequest:_req
                                            delegate:self
                                    startImmediately:NO];
    
    [_conn start];
    
    while (_conn != nil) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                 beforeDate:[NSDate distantFuture]];
    }
}

- (void)setResponseFile:(NSString *)respFilePath
{
    if (_responseStream != nil) {
        [_responseStream close];
        _responseStream = nil;
    }
    
    _responseStream = [[NSOutputStream alloc] initToFileAtPath:respFilePath append:NO];
    [_responseStream open];
}

- (NSError *)connectionError
{
    return _error;
}

#pragma mark -- NSURLConnectionDataDelegate

// 网络出错
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    _error = error;
    
    if ([self isCancelled]) {
        return;
    }
    
    if (self.delegate != nil &&
        [self.delegate respondsToSelector:@selector(httpFileTransfer:userObj:didFailWithError:)]) {
        [self.delegate httpFileTransfer:_requestId userObj:_userObj didFailWithError:error];
    }
    
    _conn = nil;
    
    if (_responseStream != nil) {
        [_responseStream close];
        _responseStream = nil;
    }
}

// 连接结束
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    if ([self isCancelled]) {
        return;
    }
    
    if (self.delegate != nil &&
        [self.delegate respondsToSelector:@selector(httpFileTransferDidFinishLoading:userObj:)]) {
        [self.delegate httpFileTransferDidFinishLoading:_requestId userObj:_userObj];
    }
    
    _conn = nil;
    
    if (_responseStream != nil) {
        [_responseStream close];
        _responseStream = nil;
    }
}

// 接收 response header
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    if ([self isCancelled]) {
        return;
    }
    
    if (self.delegate != nil &&
        [self.delegate respondsToSelector:@selector(httpFileTransfer:userObj:didReceiveResponse:)]) {
        [self.delegate httpFileTransfer:_requestId userObj:_userObj didReceiveResponse:response];
    }
}

// 接收 response body
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    if ([self isCancelled]) {
        return;
    }
    
    if (self.delegate != nil &&
        [self.delegate respondsToSelector:@selector(httpFileTransfer:userObj:didReceiveData:)]) {
        [self.delegate httpFileTransfer:_requestId userObj:_userObj didReceiveData:data];
    }
    
    if (_responseStream != nil) {
        [_responseStream write:[data bytes] maxLength:[data length]];
    }
}

// 发送数据
- (void)connection:(NSURLConnection *)connection
   didSendBodyData:(NSInteger)bytesWritten
 totalBytesWritten:(NSInteger)totalBytesWritten
totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
{
    if ([self isCancelled]) {
        return;
    }
    
    if (self.delegate != nil &&
        [self.delegate respondsToSelector:@selector(httpFileTransfer:userObj:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:)]) {
        [self.delegate httpFileTransfer:_requestId userObj:_userObj
                         didSendBodyData:bytesWritten
                       totalBytesWritten:totalBytesWritten
               totalBytesExpectedToWrite:totalBytesExpectedToWrite];
    }
}

@end    // implementation XMQHttpTransfer


@interface XMQFileTansferRequest ()
{
    NSOperationQueue *_queue;
}

@end


@implementation XMQFileTansferRequest

- (id)init
{
    self = [super init];
    if (self != nil) {
        _queue = [[NSOperationQueue alloc] init];
    }
    
    return self;
}

- (id)initWithOperationCount:(NSInteger)count
{
    self = [self init];
    if (self != nil) {
        [_queue setMaxConcurrentOperationCount:count];
    }
    
    return self;
}

- (void)addAsyncRequest:(NSNumber *)requestId
                userObj:(id)userObj
                 method:(NSString *)method
                    url:(NSString *)url
               withBody:(NSData *)body
      waitUntilFinished:(BOOL)waitUntilFinished
                  error:(NSError **)error
{
    XMQHttpTransfer *httpTansfer = [[XMQHttpTransfer alloc] initWithRequest:requestId
                                                                    userObj:userObj
                                                                     method:method
                                                                        url:url
                                                                   withBody:body];
    httpTansfer.delegate = self.delegate;
    
    [_queue addOperation:httpTansfer];
    
    if (waitUntilFinished) {
        [self waitUntilAllOperationsAreFinished];
        if (error != NULL) {
            *error = [httpTansfer connectionError];
        }
    }
}

- (void)addAsyncRequest:(NSNumber *)requestId
                userObj:(id)userObj
                 method:(NSString *)method
                    url:(NSString *)url
               withBodyFile:(NSString *)bodyFile
      waitUntilFinished:(BOOL)waitUntilFinished
                  error:(NSError **)error
{
    XMQHttpTransfer *httpTansfer = [[XMQHttpTransfer alloc] initWithRequest:requestId
                                                                    userObj:userObj
                                                                     method:method
                                                                        url:url
                                                                   withBodyFile:bodyFile];
    httpTansfer.delegate = self.delegate;
    
    [_queue addOperation:httpTansfer];
    
    if (waitUntilFinished) {
        [self waitUntilAllOperationsAreFinished];
        if (error != NULL) {
            *error = [httpTansfer connectionError];
        }
    }
}

- (void)addAsyncRequest:(NSNumber *)requestId
                userObj:(id)userObj
                 method:(NSString *)method
                    url:(NSString *)url
               withBody:(NSData *)body
               responseFile:(NSString *)responseFile
      waitUntilFinished:(BOOL)waitUntilFinished
                  error:(NSError **)error
{
    XMQHttpTransfer *httpTansfer = [[XMQHttpTransfer alloc] initWithRequest:requestId
                                                                    userObj:userObj
                                                                     method:method
                                                                        url:url
                                                                   withBody:body];
    httpTansfer.delegate = self.delegate;
    [httpTansfer setResponseFile:responseFile];
    
    [_queue addOperation:httpTansfer];
    
    if (waitUntilFinished) {
        [self waitUntilAllOperationsAreFinished];
        if (error != NULL) {
            *error = [httpTansfer connectionError];
        }
    }
}

- (void)addAsyncRequest:(NSNumber *)requestId
                userObj:(id)userObj
                 method:(NSString *)method
                    url:(NSString *)url
           withBodyFile:(NSString *)bodyFile
           responseFile:(NSString *)responseFile
      waitUntilFinished:(BOOL)waitUntilFinished
                  error:(NSError **)error
{
    XMQHttpTransfer *httpTansfer = [[XMQHttpTransfer alloc] initWithRequest:requestId
                                                                    userObj:userObj
                                                                     method:method
                                                                        url:url
                                                               withBodyFile:bodyFile];
    httpTansfer.delegate = self.delegate;
    [httpTansfer setResponseFile:responseFile];
    
    [_queue addOperation:httpTansfer];
    
    if (waitUntilFinished) {
        [self waitUntilAllOperationsAreFinished];
        if (error != NULL) {
            *error = [httpTansfer connectionError];
        }
    }
}

- (NSUInteger)currentOperationCount
{
    NSUInteger count = [_queue operationCount];
    if (count > 0) {
        count--;
    }
    
    return count;
}

- (void)waitUntilAllOperationsAreFinished
{
    [_queue waitUntilAllOperationsAreFinished];
}

- (void)cancelAllOperations
{
    [_queue cancelAllOperations];
}

@end    // implementation XMQFileTansferRequest
